import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

public class DalooSynth {

	// The following are general instance variables
	// used to create a SourceDataLine object.
	AudioFormat audioFormat;
	AudioInputStream audioInputStream;
	SourceDataLine sourceDataLine;

	// The following are audio format parameters.
	// They may be modified by the signal generator
	// at runtime. Values allowed by Java
	// SDK 1.4.1 are shown in comments.
	float sampleRate = 16000.0F;
	// Allowable 8000,11025,16000,22050,44100
	int sampleSizeInBits = 16;
	// Allowable 8,16
	int channels = 1;
	// Allowable 1,2
	boolean signed = true;
	// Allowable true,false
	boolean bigEndian = true;
	// Allowable true,false

	// A buffer to hold two seconds monaural and one
	// second stereo data at 16000 samp/sec for
	// 16-bit samples
	byte audioData[] = new byte[16000 * 4];

	public static Double DEMI_TON = Math.sqrt(Math.sqrt(Math.cbrt(2)));
	public static double LA_440 = 440;
	public static double DO = LA_440 * Math.pow(DEMI_TON, 3);

	// -------------------------------------------//
	public static void main(String args[]) throws Exception {
		System.err.println(DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON);
		System.err
				.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON);
		System.err.println(DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON * DEMI_TON
				* DEMI_TON * DEMI_TON);

		new DalooSynth().playNoteInNewThread(DO);
		// // Thread.sleep(500);
		// // new DalooSynth().playNoteInNewThread(587);
		// // Thread.sleep(500);
		// new DalooSynth().playNoteInNewThread(getFreq(3));
		// // Thread.sleep(500);
		// // new DalooSynth().playNoteInNewThread(698.5);
		// // Thread.sleep(500);
		new DalooSynth().playNoteInNewThread(getFreq(7));
		// // Thread.sleep(500);
		// // new DalooSynth().playNoteInNewThread(880);
		// // Thread.sleep(500);
		// // new DalooSynth().playNoteInNewThread(988);
		// // Thread.sleep(500);
		// new DalooSynth().playNoteInNewThread(getFreq(10));
		new DalooSynth().playNoteInNewThread(getFreq(12));

	}// end main
		// -------------------------------------------//

	public static double getFreq(int index) {
		double d = DO;

		d = d * Math.pow(DEMI_TON, index);

		return d;
	}

	//
	// public static double getNote(double freq, int nbHalfTons) {
	// double d=freq;
	// int i=0;
	//
	// while(i<nbHalfTons) {
	// d = d*DEMI_TON;
	// i++;
	// }
	//
	// return d;
	// }

	public DalooSynth() throws InterruptedException {// constructor
		// playOrFileData(523);

	}// end constructor
		// -------------------------------------------//

	public void playNoteInNewThread(double freq) {
		new ListenThread(freq).start();
	}

	// This method plays or files the synthetic
	// audio data that has been generated and saved
	// in an array in memory.
	private void playOrFileData(double freq) {
		try {
			byte audioData[] = new byte[16000 * 4];
			new SynGen().getSyntheticData(audioData, freq);

			// Get an input stream on the byte array
			// containing the data
			InputStream byteArrayInputStream = new ByteArrayInputStream(
					audioData);

			// Get the required audio format
			audioFormat = new AudioFormat(sampleRate, sampleSizeInBits,
					channels, signed, bigEndian);

			// Get an audio input stream from the
			// ByteArrayInputStream
			audioInputStream = new AudioInputStream(byteArrayInputStream,
					audioFormat, audioData.length / audioFormat.getFrameSize());

			// Get info on the required data line
			DataLine.Info dataLineInfo = new DataLine.Info(
					SourceDataLine.class, audioFormat);

			// Get a SourceDataLine object
			sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
			// Decide whether to play the synthetic
			// data immediately, or to write it into
			// an audio file, based on the user
			// selection of the radio buttons in the
			// South of the GUI..
			// Create a thread to play back the data and
			// start it running. It will run until all
			// the data has been played back
			// new ListenThread().start();

			try {
				byte playBuffer[] = new byte[16384];

				// Open and start the SourceDataLine
				sourceDataLine.open(audioFormat);
				sourceDataLine.start();

				int cnt;
				// Get beginning of elapsed time for
				// playback
//				long startTime = new Date().getTime();

				// Transfer the audio data to the speakers
				while ((cnt = audioInputStream.read(playBuffer, 0,
						playBuffer.length)) != -1) {
					// Keep looping until the input read
					// method returns -1 for empty stream.
					if (cnt > 0) {
						// Write data to the internal buffer of
						// the data line where it will be
						// delivered to the speakers in real
						// time
						sourceDataLine.write(playBuffer, 0, cnt);
					}// end if
				}// end while

				// Block and wait for internal buffer of the
				// SourceDataLine to become empty.
				sourceDataLine.drain();

				// Get and display the elapsed time for
				// the previous playback.
//				int elapsedTime = (int) (new Date().getTime() - startTime);

				// Finish with the SourceDataLine
				sourceDataLine.stop();
				sourceDataLine.close();

			} catch (Exception e) {
				e.printStackTrace();
				System.exit(0);
			}// end catch

		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}// end catch
	}// end playOrFileData
		// =============================================//

	// Inner class to play back the data that was
	// saved.
	class ListenThread extends Thread {
		// This is a working buffer used to transfer
		// the data between the AudioInputStream and
		// the SourceDataLine. The size is rather
		// arbitrary.
		byte playBuffer[] = new byte[16384];
		double freq;

		public ListenThread(double freq) {
			this.freq = freq;
		}

		public void run() {
			playOrFileData(freq);

		}// end run
	}// end inner class ListenThread
		// =============================================//

	// Inner signal generator class.

	// An object of this class can be used to
	// generate a variety of different synthetic
	// audio signals. Each time the getSyntheticData
	// method is called on an object of this class,
	// the method will fill the incoming array with
	// the samples for a synthetic signal.
	class SynGen {
		// Note: Because this class uses a ByteBuffer
		// asShortBuffer to handle the data, it can
		// only be used to generate signed 16-bit
		// data.
		ByteBuffer byteBuffer;
		ShortBuffer shortBuffer;
		int byteLength;

		void getSyntheticData(byte[] synDataBuffer, double freq) {
			// Prepare the ByteBuffer and the shortBuffer
			// for use
			byteBuffer = ByteBuffer.wrap(synDataBuffer);
			shortBuffer = byteBuffer.asShortBuffer();

			byteLength = synDataBuffer.length;
			tones(freq);

		}// end getSyntheticData method
			// -------------------------------------------//

		// This method generates a monaural triple-
		// frequency pulse that decays in a linear
		// fashion with time.
		void decayPulse(double freq) {
			channels = 1;// Java allows 1 or 2
			int bytesPerSamp = 2;// Based on channels
			sampleRate = 16000.0F;
			// Allowable 8000,11025,16000,22050,44100
			int sampLength = byteLength / bytesPerSamp;
			for (int cnt = 0; cnt < sampLength; cnt++) {
				// The value of scale controls the rate of
				// decay - large scale, fast decay.
				double scale = 2 * cnt;
				if (scale > sampLength)
					scale = sampLength;
				double gain = 16000 * (sampLength - scale) / sampLength;
				double time = cnt / sampleRate;
				// double freq = 499.0;// an arbitrary freq
				double sinValue = (Math.sin(2 * Math.PI * freq * time)
						+ Math.sin(2 * Math.PI * (freq / 1.8) * time) + Math
						.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0;
				shortBuffer.put((short) (gain * sinValue));
			}// end for loop
		}// end method decayPulse
			// -------------------------------------------//

		// This method generates a monaural tone
		// consisting of the sum of three sinusoids.
		void tones(double freq) {
			channels = 1;// Java allows 1 or 2
			// Each channel requires two 8-bit bytes per
			// 16-bit sample.
			int bytesPerSamp = 2;
			sampleRate = 16000.0F;
			// Allowable 8000,11025,16000,22050,44100
			int sampLength = byteLength / bytesPerSamp;
			for (int cnt = 0; cnt < sampLength; cnt++) {
				double time = cnt / sampleRate;
				double sinValue = (Math.sin(2 * Math.PI * freq * time)
						+ Math.sin(2 * Math.PI * (freq / 1.8) * time) + Math
						.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0;
				shortBuffer.put((short) (16000 * sinValue));
			}// end for loop
		}// end method tones
			// -------------------------------------------//

		// -------------------------------------------//
	}// end SynGen class
		// =============================================//

}// end outer class DalooSynth.java